﻿using System;
using System.Collections.Generic;
using System.Text;
//MicroDesigner
using Jc.MicroDesigner.Management;
using System.Reflection;
using System.IO;
using System.Xml;

namespace Jc.MicroDesigner.Objects
{
    /// <summary>
    /// Элементы для построения схем
    /// </summary>
    public abstract class CircuitElement : BaseElement
    {
        /// <summary>
        /// Пины элемента
        /// </summary>
        protected List<PIN> _PINCollection = new List<PIN>();

        /// <summary>
        /// Схема
        /// </summary>
        protected ObjectManager _Manager = null;

        /// <summary>
        /// Файл конфигурации
        /// </summary>
        protected string _Config = "";

        /// <summary>
        /// Выводы элемента
        /// </summary>
        public List<PIN> PINs
        {
            get
            {
                return _PINCollection;
            }
        }

        /// <summary>
        /// Файл конфигурации
        /// </summary>
        public string Config
        {
            get
            {
                return _Config;
            }
        }

        /// <summary>
        /// Создать элемент цепи
        /// </summary>
        /// <param name="name">Имя</param>
        /// <param name="pins_count">Количество PIN</param>
        public CircuitElement(string name, uint pins_count)
            : base(name)
        {
            for (int i = 1; i <= pins_count; i++)
            {
                _PINCollection.Add(this.CreateSpecialPIN());
            }
        }

        /// <summary>
        /// Загрузить информацию об элементе из xml
        /// </summary>
        /// <param name="manager">Схема</param>
        /// <param name="config">Путь к файлу конфигурации</param>
        public CircuitElement(ObjectManager manager, string config)
        {
            try
            {
                _Manager = manager;
                _Config = config;
                char dirsep = Path.DirectorySeparatorChar;
                XmlDocument doc = new XmlDocument();

                string conf_dir = Path.Combine(manager.AppPath, "Config" + dirsep);
                string[] prefixes = new string[] { "^", "#", "@" };
                string[] folders = new string[] { 
                            Path.Combine(conf_dir,"Elements" + dirsep), 
                            Path.Combine(conf_dir,"Schemes" + dirsep),
                            Path.Combine(conf_dir,"User" + dirsep)
                    };

                string config_path = GetFilePath(folders, config); 
                if (!File.Exists(config_path))
                    throw new Exception("Отсутствует файл конфигурации: \n" + config_path);
                doc.Load(config_path);
                FileInfo config_file = new FileInfo(config_path);
                XmlNode root = null;
                int i = 0;
                while ((i < doc.ChildNodes.Count) && (doc.ChildNodes[i].Name != "root"))
                    i++;
                if (i < doc.ChildNodes.Count)
                {
                    root = doc.ChildNodes[i];
                    XmlNode item = root.FirstChild;
                    //заполняем свойства элемента
                    this.Name = item.Attributes["name"].Value;
                    this.Description = item.Attributes["description"].Value;
                    string img_path = item.Attributes["image"].Value;
                    string appimages = Path.Combine(manager.AppPath, "Images" + dirsep);
                    folders = new string[] { 
                            appimages + "Elements", 
                            appimages + "Schemes",
                            config_file.Directory.FullName
                    };
                    this._DrawOptions.ImageFile = GetFilePath(folders, img_path);
                    //анализируем дополнительные свойства
                    XmlNode properties = item.ChildNodes[0];
                    Type object_type = this.GetType();
                    foreach (XmlNode node in properties.ChildNodes)
                    {
                        string name = node.Attributes["name"].Value;
                        string value = node.Attributes["value"].Value;
                        PropertyInfo prop_inf = object_type.GetProperty(name);
                        if (prop_inf != null)
                        {
                            object val = Convert.ChangeType(value, prop_inf.PropertyType);
                            prop_inf.SetValue(this, val, null);
                        }
                    }
                    //анализируем PIN'ы
                    XmlNode pins = item.ChildNodes[1];
                    foreach (XmlNode node in pins.ChildNodes)
                    {
                        PIN p = CreateSpecialPIN();
                        p.Init(node);
                        _PINCollection.Add(p);
                    }
                    //анализируем внутреннюю информацию
                    XmlNode additionals = item.ChildNodes[2];
                    LoadConfigProps(additionals);
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Ошибка создания элемента\nНевозможно создать элемент: " + this.GetType().Name
                    + "\n" + ex.Message, ex);
            }
        }

        /// <summary>
        /// Служебный конструктор без загрузки конфигурации
        /// </summary>
        /// <param name="name">Имя элемента</param>
        protected CircuitElement(string name) : base(name) { }

        #region Methods

        /// <summary>
        /// Загрузить дополнительную информацию элемента из его конфига
        /// </summary>
        /// <param name="additionals">Ветвь с дополнительной иформацией</param>
        protected virtual void LoadConfigProps(XmlNode additionals) { }

        /// <summary>
        /// Создает PIN-элементы со свойствами, характерными данному элементу
        /// </summary>
        /// <returns></returns>
        protected virtual PIN CreateSpecialPIN()
        {
            return new PIN((_PINCollection.Count + 1).ToString(), this);
        }

        /// <summary>
        /// Найти пин по имени
        /// </summary>
        /// <param name="name">Имя пина</param>
        /// <returns>Пин</returns>
        public PIN GetPINByName(string name)
        {
            PIN res = null;
            int i = 0;
            while ((i < _PINCollection.Count) && (_PINCollection[i].Name != name))
                i++;
            if (i < _PINCollection.Count)
                res = _PINCollection[i];
            return res;
        }

        /// <summary>
        /// Разрушить все соединения
        /// </summary>
        public void DisconnectAll()
        {
            for (int i = 0; i < _PINCollection.Count; i++)
                _PINCollection[i].Disconnect();
        }

        /// <summary>
        /// Привести элемент в исходное состояние
        /// </summary>
        public override void Refresh()
        {
            for (int i = 0; i < _PINCollection.Count; i++)
                _PINCollection[i].Refresh();
        }

        /// <summary>
        /// Выполнить полезную работу
        /// </summary>
        /// <returns>Были ли изменения ?</returns>
        public virtual bool PerformAction()
        {
            return false;
        }

        /// <summary>
        /// Отправить сигналы
        /// </summary>
        public virtual bool SendData()
        {
            return false;
        }

        /// <summary>
        /// Получить строку с заменённым псевдонимом
        /// </summary>
        /// <param name="prefixes">Префиксы путей</param>
        /// <param name="folders">Пути</param>
        /// <param name="name">Имя</param>
        /// <returns>Замена</returns>
        private static string GetReplacedString(string[] prefixes, string[] folders, string name)
        {
            string res = "";
            int fi = 0;
            while ((fi < prefixes.Length) && (name.IndexOf(prefixes[fi]) < 0))
                fi++;
            if (fi < prefixes.Length)
            {
                res = name.Replace(prefixes[fi], folders[fi] + Path.DirectorySeparatorChar);
            }
            return res;
        }

        /// <summary>
        /// Получить расположение файла
        /// </summary>
        /// <param name="paths">Возможные пути</param>
        /// <param name="name">Имя</param>
        /// <returns>Путь к файлу</returns>
        private static string GetFilePath(string[] paths, string name)
        {
            string res = "";
            int fi = 0;
            while ((fi < paths.Length) && (!File.Exists(Path.Combine(paths[fi], name))))
                fi++;
            if (fi < paths.Length)
            {
                res = Path.Combine(paths[fi], name);
            }
            return res;
        }

        /// <summary>
        /// Возвращает имя элемента в файле конфигурации
        /// </summary>
        /// <returns></returns>
        public static string AnalyzeConfig(ObjectManager manager, string config)
        {
            string res = "";
            try
            {
                char dirsep = Path.DirectorySeparatorChar;
                XmlDocument doc = new XmlDocument();

                string conf_dir = Path.Combine(manager.AppPath, "Config" + dirsep);
                string[] folders = new string[] { 
                            Path.Combine(conf_dir,"Elements" + dirsep), 
                            Path.Combine(conf_dir,"Schemes" + dirsep),
                            Path.Combine(conf_dir,"User" + dirsep)
                    };

                string config_path = GetFilePath(folders, config);
                doc.Load(config_path);
                FileInfo config_file = new FileInfo(config_path);
                XmlNode root = null;
                int i = 0;
                while ((i < doc.ChildNodes.Count) && (doc.ChildNodes[i].Name != "root"))
                    i++;
                if (i < doc.ChildNodes.Count)
                {
                    root = doc.ChildNodes[i];
                    XmlNode item = root.FirstChild;
                    //заполняем свойства элемента
                    res = item.Attributes["name"].Value;
                }
            }
            catch (Exception ex)
            {
                Exceptions.ExceptionDialog.ShowException(ex,
                    "Ошибка", "Ошибка анализа элемента\n" + config);
            }
            return res;
        }

        #endregion
    }
}